Elegant Drag N Drop
Volume Number: 10
Issue Number: 11
Column Tag: Implementor’s Journal
Related Info: TextEdit
Implementing Elegant Drag and Drop
for Styled Text Fields
An implementor’s journal of adding drag and drop to SmalltalkAgents®
By David Simmons, Quasar Knowledge Systems
Note: Source code files accompanying article are located on MacTech CD-ROM or
source code disks.
Utilizing the drag manager effectively in your applications can be difficult, but it
doesn’t have to be. In this article I will be providing a reconstructed journal of my
efforts in putting in drag and drop support into SmalltalkAgents styled text field
components ( class). However, before we jump into the code and annotated
explanations, I am going to provide a little background information to set the stage.
The design of SmalltalkAgents direct manipulation interface for the Agents Object
System (AOS) Presentation Manager (APM) View System required that we architect
specific drag and drop functionality into existing components. One of the more
challenging designs was that of the styled text field component. Apple’s sample
applications that came with their Drag and Drop Manager toolkit are quite well done
and have a number of subtleties. We wanted to be sure that our Smalltalk
implementation was up to the Macintosh user-interface standards.
SmalltalkAgents’ Drag and Drop Architecture was derived by examining a large
number of applications existing on different operating systems. We wanted to
understand how they handled various drag and drop operations including text handling.
We also examined the protocols and frameworks for X/Motif, MS-Windows, OpenDoc,
Apple Drag and Drop, and Taligent’s architecture to ensure that our framework was
generic enough that we could transparently plug into host system frameworks when
they were available; and when they were not, we wanted SmalltalkAgents internal
framework to have a full-featured architecture of its own.
To accomplish these goals we felt that we had to provide a complete internal
Smalltalk architecture that could stand on its own as well as transparently integrating
with a given host system. This meant that if a given host system was missing any
features our architecture would dynamically supply the missing functionality.
Throughout the remainder of this article I will usually refer to various generic
features of SmalltalkAgents framework, the first time I do so, I will try to present the
equivalent Apple Drag Manager issues as well as explanations for non-Smalltalk
programmers.
Background
As part of preparing the reader, we need to cover some of the basics of what a
component performing drag operations is responsible for. Drag operations can be
broken down into three distinct areas. First is the initiation of a drag operation,
second is the tracking and visual drag acceptance feedback, and third is the drop
handling.
Drag initiation for text fields involves modification of the button-press handlers
of text components. Specifically it requires that an additional test be made on a
button-press to determine if the click occurred inside a selection range of one or more
characters. If the test was true then a drag package must be built and the drag manager
invoked to handle the dragging and possible dropping.
Drag tracking involves three subsidiary operations for handling a drag action.
The first is the drag-enter test where the text-field determines if it has any interest
in the drag-package. If it does, then it usually is necessary to allocate scratch data for
the tracking within operation. Assuming that it does, the second phase involves
tracking the drag operation while the cursor is inside the text field (i.e., drag-within
hiliting and auto-scrolling). The third phase is the cleanup operation that the
text-field must perform when the cursor exits the text field (i.e., the drag-exit
de-hiliting and release of any allocated scratch data).
The drop handling operation is based on a callback to the component (in our case,
a text-field) in which the drop occurred. The drop operation involves a number of
distinct steps: the first step is to determine if the operation was a self move, or a copy
operation. If it was a copy operation then it is simple. If it was a self move operation
then we need to handle the animation of the text move within the text field.
And Now The Code!
As a starting point I began the work in the drag tracking methods
inherited from the class. As a point of reference, in Smalltalk
terminology a “method” is equivalent to the C term “function”. My first task was the
filtering of drag requests so that I could determine whether or not to display any form
of visible user-feedback to indicate whether or not the dragged package of items
contained something of interest for my text field. To do that I needed to specialize the
<#dragEnter> method in the class.
/* 1 */
TEField[i]dragEnter
“Validate whether the package is interesting”
(Mouse dragPackageDetect:
[:type | (type isKindOf: String class)])
ifFalse: [^self].
In the above code block we are using <#dragPackageDetect:> method to iterate
over the contents of the drag package and inject the “type-of-object” that would be
instantiated if we were to ask for the drag package contents (or some subportion of the
contents).
If nothing interesting is in the drag package then we simply exit via “^self”.
Note: The drag package is a “promise” of what will be delivered and that in most
cases the actual data is not instantiated (reified or made-real ) until you ask for it.
/* 2 */
“Compute the drag area and then display it”
outer := self contentBounds asRegion copy insetBy: (-1 @ -1).
inner := outer copy insetBy: 2.
outer differenceWith: inner.
Here we are computing the content region of our text-field ( instance)
and then creating a 2 pixel wide region that surrounds it.
/* 3 */
Mouse showDragHiliteRegion: outer.

Now tell the Smalltalk drag-manager object (i.e., the ; an instance of
) to display the hilite region. We must ask our drag manager to display
the hilite area because it is managing the display of the drag-image and we want to be
assured that our hilite region is drawn “visually-behind” the drag-image.